package org.myh.excel.concurrent;


import com.monitorjbl.xlsx.StreamingReader;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.myh.excel.ExcelReader;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ClassName: AbstractDivisibleExcelBuffer
 * Description: 抽象类，由子类定义以何种方式读取文件以及要返回的类型
 * date: 2024/3/25 20:41
 *
 * @author Yu Han
 * @since JDK 1.8
 */
public abstract class AbstractDivisibleExcelBuffer<T> implements ExcelReader, Iterator<HashMap<Integer, List<T>>> {

    /**
     * 状态: 未读,正在读,已读
     */
    public final static int NOT_READ = -1;
    public final static int FINISHED = 0;
    public final static int READING = 1;
    public final static int BE_DOING = 2;
    public final static int CONSUMPTION = 3;

    /**
     * 操作的类型
     */
    private Class<T> clazz;

    /**
     *  工作簿
     */
    protected  Workbook workbook;


    /**
     *  工作表
     */
    protected Sheet sheet;

    /**
     * 表格总行数
     */
    protected  int rowNum;

    /**
     * 每次缓存的行数
     */
    protected final int rowCacheSize;

    protected AtomicInteger readState = new AtomicInteger(NOT_READ);

    protected InputStream stream;

    /**
     * 并发读返回的数据
     */
    protected ConcurrentLinkedDeque<HashMap<Integer, List<T>>> deque = new ConcurrentLinkedDeque<>();

    /**
     * 单线程回调
     */
    private ExcelDataCallBack callBack;

    /**
     * @param inputStream
     * @param bufferSize   设置缓存的大小
     * @param rowCacheSize 缓存行的数量，也就是每次读取多少行到内存中，而不是一下子全都加载进内存
     * @param sheetIndex   读取的表格下标
     */
    public AbstractDivisibleExcelBuffer(InputStream inputStream, int bufferSize, int rowCacheSize, int sheetIndex, Class<T> clazz) {
        //TODO:这里需要做一些非空验证
        this.stream = inputStream;
        this.workbook = StreamingReader.builder()
                .bufferSize(bufferSize)
                .rowCacheSize(rowCacheSize)
                .open(inputStream);
        this.rowCacheSize = rowCacheSize;
        this.clazz = clazz;
        //获取工作表
        refresh(sheetIndex);
    }

    @Override
    public void refresh(int sheetIndex) {
        this.sheet = workbook.getSheetAt(sheetIndex);
        this.rowNum = sheet.getLastRowNum();
    }

    public Class<T> getClazz() {
        return clazz;
    }


    @Override
    public void closeResource() throws IOException {
        // 7.释放资源
        workbook.close();
        stream.close();
    }

    @Override
    public boolean hasNext() {
        // 1 可能有还未开始 但是分线程结束了
        // 2 如有数据返回true
        // 3 正在进行
        if (readState.get() == NOT_READ || readState.get() == READING || readState.get() == BE_DOING || deque.size() > 0) {
            return true;
        }

        //已经设置了结束标识，若是还存在数据 则返回true
        if (readState.get() == FINISHED && deque.size() > 0) {
            return true;
        }

        //结束状态且没有数据
        if (readState.get() == FINISHED && deque.size() <= 0) {
            readState.set(CONSUMPTION);
            return false;
        }

        return false;
    }

    @Override
    public HashMap<Integer, List<T>> next() {
        return (HashMap<Integer, List<T>>) deque.pollLast();
    }

    public static interface ExcelDataCallBack<T>{
        /**
         * 发送此行
         * @param index 表数据下标
         * @param rowData 行数据
         * @param total
         * @param hasNext 是否有下一个
         */
        void sendRowData(int index, T rowData,int total,boolean hasNext);
    }

    public void setCallBack(ExcelDataCallBack callBack) {
        this.callBack = callBack;
    }

    public ExcelDataCallBack getCallBack() {
        return callBack;
    }
}
